﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;

namespace Hbrs.Soa.Restclient.Client
{
    #region RestStarterClientBase

    /// <summary>Base class for calling REST-based webservice created by the REST starter kit</summary>
    /// <typeparam name="T">The type of the resource provided by the REST service</typeparam>
    public abstract class RestStarterClientBase<T> : RestClientBase<T> where T : class, new()
    {
        #region type ItemElement

        class ItemElement
        {
            public string Id { get; set; }
            public XElement Element { get; set; }

            public static ItemElement FromXElement(XElement el)
            {
                return new ItemElement()
                {
                    Id = new Uri(el.Element("EditLink").Value).Segments.Last(),
                    Element = new XElement(XName.Get(typeof(T).Name), el.Element("Item").Descendants())
                };
            }
        }

        #endregion

        #region private methods

        static IEnumerable<XElement> SimpleStreamAxis(XmlReader reader, string matchName)
        {
            reader.MoveToContent();
            bool readDone = false; // XElement.ReadFrom changes position of Reader
            while (readDone || reader.Read())
            {
                readDone = false;
                switch (reader.NodeType)
                {
                    case XmlNodeType.Element:
                        if (reader.Name == matchName)
                        {
                            XElement el = XElement.ReadFrom(reader) as XElement;
                            readDone = true;
                            if (el != null)
                                yield return el;
                        }
                        break;
                }
            }
            reader.Close();
        }

        #endregion

        #region protected methods

        protected override XmlReader TransformResponseContent(Stream response)
        {
            XmlReaderSettings readerSettings = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment };
            XmlReader srcReader = XmlReader.Create(response, readerSettings);
            srcReader.MoveToContent();
            if (srcReader.Name == "ItemInfo")
            {
                XElement el = XElement.ReadFrom(srcReader) as XElement;
                ItemElement itemElement = ItemElement.FromXElement(el);
                itemElement.Element.Add(new XAttribute(XName.Get("id"), itemElement.Id));
                response.Close();

                Stream ms = new MemoryStream();
                XmlWriterSettings settings = new XmlWriterSettings() { CloseOutput = false, ConformanceLevel = ConformanceLevel.Fragment };
                using (XmlWriter writer = XmlWriter.Create(ms, settings))
                {
                    itemElement.Element.WriteTo(writer);
                    writer.Close();
                }
                ms.Position = 0;
                srcReader = XmlReader.Create(ms, new XmlReaderSettings() { CloseInput = true, ConformanceLevel = ConformanceLevel.Fragment });
            }
            return srcReader;
        }

        protected override XmlReader TransformResponseContentList(Stream response)
        {
            List<ItemElement> elements = null;
            XmlReaderSettings readerSettings = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment };
            using (XmlReader srcReader = XmlReader.Create(response, readerSettings))
            {
                var items =
                    from el in SimpleStreamAxis(srcReader, "ItemInfo")
                    select ItemElement.FromXElement(el);
                elements = items.ToList();
            }
            elements.ForEach(element =>
            {
                element.Element.Add(new XAttribute(XName.Get("id"), element.Id));
            });
            response.Close();

            Stream ms = new MemoryStream();
            XmlWriterSettings settings = new XmlWriterSettings() { CloseOutput = false, ConformanceLevel = ConformanceLevel.Fragment };
            using (XmlWriter writer = XmlWriter.Create(ms, settings))
            {
                writer.WriteStartElement(typeof(T).Name + "List");
                elements.ForEach(element => element.Element.WriteTo(writer));
                writer.WriteEndElement();
                writer.Close();
            }
            ms.Position = 0;
            XmlReader listReader = XmlReader.Create(ms, new XmlReaderSettings() { CloseInput = true, ConformanceLevel = ConformanceLevel.Fragment });
            return listReader;
        }

        #endregion

        #region constructor, destructor

        /// <summary>Initializes a new instance of the class</summary>
        public RestStarterClientBase()
            : base()
        {
        }

        /// <summary>Initializes a new instance of the class</summary>
        /// <param name="serviceName">The name of the referenced service</param>
        public RestStarterClientBase(string serviceName)
            : base(serviceName)
        {
        }

        /// <summary>Initializes a new instance of the class</summary>
        /// <param name="baseUr">The base-uri for the resouces of the service</param>
        public RestStarterClientBase(Uri baseUri)
            : base(baseUri)
        {
        }

        #endregion
    }

    #endregion
}
